﻿using MessagePack;
using MessagePack.Formatters;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;

namespace BinaryTest
{
    class MessagePackHack
    {
        public static void Hack()
        {
            Type tp = typeof(MessagePackSerializer);
            PropertyInfo property = tp.GetProperty("DefaultResolver", BindingFlags.Static | BindingFlags.Public);
            if (property==null)
            {
                #region Version 2.x
                property = tp.GetProperty("DefaultOptions", BindingFlags.Static | BindingFlags.Public);
                if(property==null)
                {
                    throw new Exception("未知的MessagePack版本，请修改此处代码或降级MessagePack为 1.x、2.x版本!");
                }
                else
                {
                    throw new Exception("暂时不支持的MessagePack2.x版本，请修改此处代码或降级MessagePack为 1.x版本!");
                }
                #endregion
            }
            else
            {
                #region Version 1.x
                property.SetValue(null, new MyResolver());
                #endregion
            }
        }

        class MyResolver : IFormatterResolver
        {
            public IMessagePackFormatter<T> GetFormatter<T>()
            {
                Type type = typeof(T);
                if(!type.IsClass||type==typeof(string))
                {
                    return MessagePack.Resolvers.StandardResolver.Instance.GetFormatter<T>();
                }
                if(type.IsGenericType)
                {
                    Type[] genType = type.GetInterfaces()
                        .Where(x => x.IsGenericType)
                        .Where(x => typeof(IEnumerable<>).IsAssignableFrom(x.GetGenericTypeDefinition()))
                        .Select(x => x.GetGenericArguments().FirstOrDefault())
                        .ToArray();
                    if(genType.Any(x=>x.IsClass&&x!=typeof(string)))
                    {
                        return (IMessagePackFormatter<T>)Activator.CreateInstance(typeof(ClassFormater<>).MakeGenericType(type)); 
                    }
                    else
                    {
                        return MessagePack.Resolvers.StandardResolver.Instance.GetFormatter<T>();
                    }
                }
                return (IMessagePackFormatter<T>)Activator.CreateInstance(typeof(ClassFormater<>).MakeGenericType(type));
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TClass"></typeparam>
        public class ClassFormater<TClass> : MessagePack.Formatters.IMessagePackFormatter<TClass>
        {
            /// <summary>
            /// 二进制反序列化
            /// </summary>
            /// <param name="bytes">一个未知大小的空间</param>
            /// <param name="offset">当前已经读到哪里了</param>
            /// <param name="formatterResolver">一个格式化管道</param>
            /// <param name="readSize">读取了多少字节</param>
            /// <returns></returns>
            public TClass Deserialize(byte[] bytes, int offset, IFormatterResolver formatterResolver, out int readSize)
            {
                if (MessagePackBinary.IsNil(bytes, offset))
                {
                    readSize = 1;
                    return default(TClass);
                }
                var formatter = formatterResolver.GetFormatterWithVerify<String>();
                string json = MessagePackBinary.ReadString(bytes, offset, out readSize);
                offset += readSize;
                return JsonConvert.DeserializeObject<TClass>(json);
            }

            /// <summary>
            /// 二进制序列化
            /// </summary>
            /// <param name="bytes">一个65535大小的空间</param>
            /// <param name="offset">传入0</param>
            /// <param name="value">待序列化的数据</param>
            /// <param name="formatterResolver">一个格式化管道</param>
            /// <returns>序列化后数据长度</returns>
            public int Serialize(ref byte[] bytes, int offset, TClass value, IFormatterResolver formatterResolver)
            {
                if (value == null)
                {
                    return MessagePackBinary.WriteNil(ref bytes, offset);
                }
                string json = JsonConvert.SerializeObject(value);
                var formater = formatterResolver.GetFormatterWithVerify<string>();
                return formater.Serialize(ref bytes, offset, json, formatterResolver);
            }
        }
    }
}
